﻿using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

namespace GameLogic
{
    /// <summary>
    /// 资源管理器基类。<br/>
    /// 子类有：
    /// 1、TextureAnimCtr.动作资源管理类
    /// 2、MapSpritesMgr.地图瓦片资源管理类
    /// </summary>
    public abstract class ResCtrBase<T>  where T : Object
    {
        /// <summary>
        /// 单个资源的加载、回调、使用记数信息j
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        private class ResInfo<T1> where T1 : Object
        {
            ///<summary>载入资源类型</summary>
            public T1 Res { set; get; }
            ///<summary>>资源名称</summary>
            public string ResName { set; get; }
            ///<summary>资源引用计数</summary>
            public int RelCount { set; get; }
            ///<summary>资源销毁剩余时间</summary>
            public float LeftTime { set; get; }
            ///<summary>是否开始加载资源</summary>
            public bool isStart { set; get; }
            ///<summary>是否加载完毕</summary>
            public bool isOver { set; get; }

            ///<summary>加载完成回调</summary>
            private Queue<Action<T1>> _callBackQueue = new Queue<Action<T1>>();


            /// <summary>
            /// 初始化资源计数管理类
            /// </summary>
            /// <param name="resName"></param>
            /// <param name="resLiveTimes"></param>
            public void Init(string resName,float resLiveTimes)
            {
                ResName = resName;
                LeftTime = resLiveTimes;
                RelCount = 0;
                isStart = false;
                isOver = false;
            }

            /// <summary>
            /// 添加加载完成回调
            /// </summary>
            /// <param name="callBack"></param>
            public void AddCallBack(Action<T1> callBack)
            {
                _callBackQueue.Enqueue(callBack);
            }

            /// <summary>
            /// 加载完成回调
            /// </summary>
            public void CallBack()
            {
                while (_callBackQueue.Count > 0)
                {
                    var callback = _callBackQueue.Dequeue();
                    ++RelCount;
                    callback(Res);
                }
            }

            /// <summary>
            /// 清空载入数据
            /// </summary>
            public void Clear()
            {
                Res = null;
                ResName = null;
                RelCount = 0;
                LeftTime = 0;
                isStart = false;
                isOver = false;
                _callBackQueue.Clear();
            }
        }

        ///<summary>资源生命周期 单位秒</summary> 
        private int resLiveTimes = 600;

        ///<summary>资源管理类最大缓存数</summary> 
        private int maxCacheCount = 20;

        ///<summary>ResInfo缓存池</summary>
        private Stack<ResInfo<T>> resInfoPool = new Stack<ResInfo<T>>();

        ///<summary>载入失败列表</summary> 
        private Dictionary<string, bool> loadFailMap = new Dictionary<string, bool>(); 

        /// <summary>已加载成功的ResInfo列表</summary> 
        private Dictionary<T, ResInfo<T>> resSuccessMap = new Dictionary<T, ResInfo<T>>();
       
        /// <summary>
        /// 所有资源(ResInfo)的列表：
        /// 1、加载时增加进来；
        /// 2、加载失败是移除（会增加到失败列表）；
        /// 3、没有引用时，超过时间也会移除 
        ///</summary> 
        private Dictionary<string, ResInfo<T>> resInfoMap = new Dictionary<string, ResInfo<T>>();

        ///<summary>资源（ResInfo）删除列表：不使用的资源到时要删除</summary> 
        private List<string> resInfoDeList = new List<string>();

        //扫描回收资源
        protected virtual void Update()
        {
            if (resInfoMap.Count == 0) return;

            foreach (var KV in resInfoMap)
            {
                var resinfo = KV.Value;
                
                //检查计数
                if (resinfo.RelCount > 0)
                {
                    resinfo.LeftTime = resLiveTimes;
                    continue;
                }

                //检查生存时间
                resinfo.LeftTime = resinfo.LeftTime - Time.fixedDeltaTime;
                if (resinfo.LeftTime > 0) continue;

                //已经死亡资源
                //从ID列表移除
                resSuccessMap.Remove(resinfo.Res);
                //卸载资源释放内存
                UnLoadRes(resinfo.Res);
                //回收管理类
                resinfo.Clear();
                if (resInfoPool.Count < maxCacheCount) 
                    resInfoPool.Push(resinfo);
                //添加到删除列表
                resInfoDeList.Add(KV.Key);
                //每帧只销毁一个死亡资源
                break;
            }

            if (resInfoDeList.Count == 0)
                return;
            for (int i = 0; i < resInfoDeList.Count; ++i)
            {
                var name = resInfoDeList[i];
                resInfoMap.Remove(name);
            }
            resInfoDeList.Clear();
        }
        
        /// <summary>
        /// 从对象上获取component资源
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private T GetResFromObj(Object obj)
        {
            //获取资源
            T res = null;
            if (obj != null)
            {
                res = obj as T;
                if (res == null)
                {
                    GameObject go = obj as GameObject;
                    if (go != null)
                        res = go.GetComponent<T>();
                }
            }
            return res;
        }

        /// <summary>
        /// 创建资源计数管理类
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private ResInfo<T> CreateResInfo(string name)
        {
            ResInfo<T> resInfo;
            //载入管理类
            if (resInfoPool.Count == 0)
                resInfo = new ResInfo<T>();
            else
                resInfo = resInfoPool.Pop();
            //初始化管理类
            resInfo.Init(name, resLiveTimes);
            return resInfo;
        }

        
        /// <summary>
        /// 通过资源名称获取ResInfo类,没有则创建
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private ResInfo<T> GetResInfo(string name)
        {
            //获取prite管理类
            if (!resInfoMap.ContainsKey(name))
            {
                ResInfo<T> resinfo = CreateResInfo(name);
                //加入管理Map
                resInfoMap.Add(resinfo.ResName, resinfo);
            }
            return resInfoMap[name];
        }

       
        /// <summary>
        /// 载入资源失败回收计数管理类
        /// </summary>
        /// <param name="resinfo"></param>
        private void LoadResFail(ResInfo<T> resinfo)
        {
            if (!loadFailMap.ContainsKey(resinfo.ResName))
            {
                loadFailMap.Add(resinfo.ResName,true);
            }
            resInfoMap.Remove(resinfo.ResName);
            resinfo.CallBack();
            resinfo.Clear();
            resInfoPool.Push(resinfo);
        }

       
        /// <summary>
        /// 载入资源成功刷新计数管理类数据
        /// </summary>
        /// <param name="resinfo"></param>
        /// <param name="res"></param>
        private void LoadResSuccess(ResInfo<T> resinfo,T res)
        {
            resinfo.Res = res;
            resinfo.isOver = true;
            resinfo.CallBack();
            resSuccessMap.Add(res, resinfo);
        }

        /// <summary>
        /// 设置资源管理器参数
        /// </summary>
        /// <param name="lifeTime">无引用生存时间,单位秒</param>
        /// <param name="maxCache">管理类缓存个数</param>
        public void SetCacheInfo(int lifeTime = 5, int maxCache = 3)
        {
            resLiveTimes = lifeTime;
            maxCacheCount = maxCache;
        }

        #region 资源异步加载

        
        /// <summary>
        /// 异步加载资源完成回调
        /// </summary>
        /// <param name="name"></param>
        /// <param name="obj"></param>
        private void AsyncLoadOver(string name, Object obj)
        {
            ResInfo<T> resinfo = GetResInfo(name);
            //ResManager.RemoveAsset(name);   //取消ResManager对资源的管理
            App.ResMgr.RemoveAsset(name);

            //如果此时已经是完成状态,说明中途被同步加载强行替代了,所以直接返回
            if (resinfo.isOver) return;

            T res = GetResFromObj(obj);
            //获取成功
            if (res != null)
            {
                LoadResSuccess(resinfo, res);
            }
            else
            {
                LoadResFail(resinfo);
            }
        }

       
        /// <summary>
        /// 异步加载资源。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="loadOver"></param>
        public void GetResAsync(string name, Action<T> loadOver)
        {
            //检查载入失败列表
            if (loadFailMap.ContainsKey(name))
            {
                loadOver(null);
                return;
            }

            ResInfo<T> resInfo = GetResInfo(name);   //获取资源类
            resInfo.AddCallBack(loadOver);   //添加回调

            //资源还未开始加载资源
            if (!resInfo.isStart) 
            {
                //开始加载
                resInfo.isStart = true;
                //ResManager.LoadAssetAsync<T>(name, AsyncLoadOver);
                App.ResMgr.LoadAssetAsync<T>(name, AsyncLoadOver);
                return;
            }

            //资源已经加载完成
            if (resInfo.isOver)
                resInfo.CallBack(); 
        }

        #endregion

        #region 资源同步加载

       
        /// <summary>
        /// 同步加载资源
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public T GetRes(string name)
        {
            //获取管理类
            ResInfo<T> resInfo = GetResInfo(name);

            //已经加载完成
            if (resInfo.isOver)
            {
                resInfo.RelCount = resInfo.RelCount + 1;
                resInfo.LeftTime = resLiveTimes;
                return resInfo.Res;
            }

            //还未加载完成
            resInfo.isStart = true;
            //var res = ResManager.LoadAsset<T>(name);    //同步加载资源
            var res = App.ResMgr.LoadAsset<T>(name);
            if (res == null)
            {
                LoadResFail(resInfo);
                return null;
            }
            LoadResSuccess(resInfo, res);
            return res;
        }

        #endregion

        #region 资源回收

        /// <summary>
        /// 卸载资源方法必须重写
        /// </summary>
        /// <param name="res"></param>
        protected abstract void UnLoadRes(T res);

       
        /// <summary>
        /// 回收资源 
        /// </summary>
        /// <param name="res"></param>
        public void Release(T res)
        {
            if (res == null)
                return;
            var resinfo = resSuccessMap.ContainsKey(res) ? resSuccessMap[res] : null;
            if (resinfo != null)
                resinfo.RelCount = resinfo.RelCount - 1;
            else
                UnLoadRes(res);
        }

        #endregion

    }



}
